#! /usr/bin/env python

# Copyright (c) 2007, Tim Stack
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of Tim Stack nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY TIM STACK ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL TIM STACK BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#
# XXX Code is pretty hacky and needs a good cleanup.
#

import os, os.path
import re
import sys
import copy
import getopt

from exceptions import *

PACKAGE_VERSION = @PACKAGE_VERSION@

VERBOSITY = 0

FILE_EXTS = [".o", ".s"]
EXTENSION = "type_info"
WEAK_ATTR = " __attribute__ ((weak))"
WEAK_PRAGMA = "#pragma weak %s"

DECL = ("extern struct reflecxx_full_type %s_%s WEAK_ATTR;\n")

def objdump_conv1(m):
    return Stabs((m.group(1), 0, 0, 0, 0))

def objdump_conv2(m):
    return Stabs((m.group(1), 0, 0, 0, 0))

OBJDUMP_FORMATS = [
    { 'pattern' : r'\d+ \w+ \d+ \d+ [\w\d]+ \d+ (.+)',
      'converter' : objdump_conv1 },
    { 'pattern' : r'[\w\d]+\s+d\s+\*UND\*\s+(.+)',
      'converter' : objdump_conv2 },
    { 'pattern' : r'\d+\s+\w+\s+\d+\s+\d+\s+[\w\d]+ \d+\s+(.+)',
      'converter' : objdump_conv2 },
    ]

class StabsElement:

    COUNT = 0
    
    def anon_name(self):
        retval = "__reflecxx_anon%d" % StabsElement.COUNT
        StabsElement.COUNT += 1
        return retval

    def rename(self, name):
        self.name = name
        return
    
    pass

class StabsArray(StabsElement):
    def __init__(self, lo, hi, elem_id):
        self.name = None
        self.lo = lo
        self.hi = hi
        self.elem_id = elem_id
        return

    def __repr__(self):
        return `self.__dict__`
    
    pass

class StabsEnum(StabsElement):
    def __init__(self, elements):
        self.name = None
        self.elements = elements
        return
    
    def __repr__(self):
        return `self.__dict__`
    
    pass

class StabsEnumElement:
    def __init__(self, name, value):
        self.name = name
        self.value = int(value)
        return
    
    def __repr__(self):
        return `self.__dict__`
    
    pass

class StabsStruct(StabsElement):
    COUNT = 0

    def __init__(self, size = 0, fields = []):
        self.name = None
        self.size = size
        self.fields = fields
        return
    
    def __repr__(self):
        return `self.__dict__`
    
    pass

class StabsStructField(StabsElement):

    def __init__(self, name, id, bitoff, bitsize):
        self.name = name
        self.id = id
        self.bitoff = int(bitoff)
        self.bitsize = int(bitsize)
        return

    def __repr__(self):
        return `self.__dict__`
        
    pass

class StabsPointer(StabsElement):
    def __init__(self, id):
        self.name = None
        self.id = id
        StabsPointer.COUNT += 1
        return
    
    def __repr__(self):
        return `self.__dict__`
    
    pass

class StabsRange(StabsElement):
    def __init__(self, lo, hi):
        self.name = None
        self.lo = lo
        self.hi = hi
        return
    
    def __repr__(self):
        return `self.__dict__`
    
    pass

class StabsTypeRef(StabsElement):
    def __init__(self, ref = ""):
        self.name = None
        self.ref = ref
        return

    def __repr__(self):
        return `self.__dict__`
    
    pass

class StabsTypeDef(StabsElement):
    def __init__(self, id = "", type = None):
        self.name = None
        self.id = id
        self.type = type
        return

    def rename(self, name):
        self.name = name
        self.type.rename(name)
        return

    def __repr__(self):
        return `self.__dict__`

    pass

class StabsParam:
    def __init__(self, type):
        self.name = None
        self.type = type
        return

    def __repr__(self):
        return `self.__dict__`

    pass

class StabsFunction(StabsElement):
    def __init__(self, ret_type):
        self.name = None
        self.ret_type = ret_type
        self.params = []
        return

    def __repr__(self):
        return `self.__dict__`

    pass

class StabsParserException(Exception):
    pass

class StabsParser:

    def __init__(self, ns):
        self.ns = ns
        self.rem = None
        self.curr_fun = None
        return

    def parseField(self, str):
        if VERBOSITY > 1:
            sys.stderr.write("trace: parseField -- %s\n" % str)
            pass

        m = re.match(r'([^:]+):(\d+|\(\d+,\d+\))', str)
        if not m:
            raise StabsParserException("Bad struct field -- %s" % str)

        self.rem = str[m.end(0):]
        if self.rem[0] == '=':
            self.ns[m.group(2)] = self.parseType(self.rem[1:])
            pass

        m2 = re.match(r',(\d+),(\d+);(.+)?', self.rem)
        if not m2:
            raise StabsParserException("Bad struct field -- %s" % str)
        self.rem = m2.group(3)
        
        return StabsStructField(
            m.group(1), m.group(2), m2.group(1), m2.group(2))

    def parseFields(self, str):
        if VERBOSITY > 1:
            sys.stderr.write("trace: parseFields -- %s\n" % str)
            pass

        retval = []
        self.rem = str
        while self.rem and self.rem[0] != ';':
            retval.append(self.parseField(self.rem))
            pass
        self.rem = self.rem[1:]

        return retval

    def parseType(self, str):
        if VERBOSITY > 1:
            sys.stderr.write("trace: parseType -- %s\n" % str)
            pass
        
        m = re.match(r'(\d+|\(\d+,\d+\))(?:=(.+))?', str)
        if m:
            pair = m.groups()
            id = pair[0]
            if pair[1]:
                retval = self.parseType(pair[1])
                self.ns[id] = retval
                pass
            else:
                retval = StabsTypeRef(id)
                self.rem = str[m.end(0):]
                pass
            pass
        elif str[0] in ("t", "T", "S", "V"):
            retval = self.parseType(str[1:])
            pass
        elif str[0] == 's':
            m = re.match(r's(\d+)(.+)', str)
            if not m:
                raise StabsParserException("Bad struct -- %s" % str)

            retval = StabsStruct(int(m.group(1)), self.parseFields(m.group(2)))
            pass
        elif str[0] == 'e':
            m = re.search(r'e(([^:]+:-?\d+,)+);', str)
            if not m:
                raise StabsParserException("Bad enum -- %s" % str)

            retval = StabsEnum([StabsEnumElement(*(e.split(':')))
                                for e in m.group(1).split(",") if e])
            self.rem = str[m.end(0):]
            pass
        elif str[0] == 'a':
            m = re.match(
                r'ar(?:\d+|\(\d+,\d+\));(\d+);(\d+);(\d+|\(\d+,\d+\))', str)
            if not m:
                raise StabsParserException("Bad array -- %s" % str)

            retval = StabsArray(*m.groups())
            if str[m.end(0):].startswith("="):
                self.parseType(str[m.start(3):])
                pass
            else:
                self.rem = str[m.end(0):]
                pass
            pass
        elif str[0] == 'r':
            m = re.match(r'r(\d+|\(\d+,\d+\));(-?\d+);(-?\d+);', str)
            if not m:
                raise StabsParserException("Bad range -- %s" % str)
            
            retval = StabsRange(m.group(2), m.group(3))
            self.rem = str[m.end(0):]
            pass
        elif str[0] == '*':
            m = re.match(r'\*(\d+|\(\d+,\d+\))(?:=(.+))?', str)
            if not m:
                raise StabsParserException("Bad pointer -- %s" % str)

            if m.group(2):
                tmp = self.parseType(m.group(2))
                self.ns[m.group(1)] = tmp
                pass
            retval = StabsPointer(m.group(1))
            self.rem = str[m.end(0):]
            pass
        elif str[0] == '@':
            semi = str.find(";")
            if semi == -1:
                raise StabsParserException("Bad type attribute -- %s" % str)
            
            retval = self.parseType(str[semi + 1:])
            pass
        elif str[0] == 'F' or str[0] == 'f':
            self.curr_fun = StabsFunction(self.parseType(str[1:]))
            retval = self.curr_fun
            pass
        elif str[0] == 'p':
            retval = StabsParam(self.parseType(str[1:]))
            pass
        else:
            raise StabsParserException("Unhandled type -- %s" % str)
        
        return retval
    
    def parse(self, str):
        if VERBOSITY > 1:
            sys.stderr.write("trace: parse -- %s\n" % str)
            pass
        
        ntls = str.split(":", 1)
        if len(ntls) != 2:
            raise StabsParserException("Malformed type -- %s" % str)

        self.rem = ntls[1]
        retval = self.parseType(self.rem)
        if isinstance(retval, StabsParam):
            if not self.curr_fun:
                raise StabsParserException(
                    "Parameter without function -- %s" % str)

            retval.name = ntls[0]
            self.curr_fun.params.append(retval)
            pass
        elif ntls[0] not in ["", " ", "$_"]:
            retval.rename(ntls[0])
            self.ns[retval.name] = retval
            pass
        return retval
    
    pass

class StabsToC:
    PRIMITIVES = {
        "char" : "RT_CHAR",
        "int" : "RT_INT",
        "short" : "RT_SHORT",
        "long int" : "RT_LONG",
        "long long int" : "RT_LONG_LONG",
        "unsigned char" : "RT_UNSIGNED_CHAR",
        "unsigned int" : "RT_UNSIGNED_INT",
        "unsigned short" : "RT_UNSIGNED_SHORT",
        "long unsigned int" : "RT_UNSIGNED_LONG",
        "long long unsigned int" : "RT_UNSIGNED_LONG_LONG",
        "float" : "RT_FLOAT",
        "double" : "RT_DOUBLE",
        "bool" : "RT_BOOL",
        "void" : "RT_VOID",
        }

    CINV_PRIMITIVES = {
        "char" : "c",
        "int" : "i",
        "short" : "s",
        "long int" : "l",
        "long long int" : "e",
        "unsigned char" : "c",
        "unsigned int" : "i",
        "unsigned short" : "s",
        "long unsigned int" : "l",
        "long long unsigned int" : "e",
        "float" : "f",
        "double" : "d",
        "void" : "",
        }

    def __init__(self, file, ns):
        self.file = file
        self.ns = ns
        
        self.file.write("/* DO NOT MODIFY THIS FILE, IT IS AUTOGENERATED */\n"
                        "\n"
                        "#include \"reflecxx.h\"\n"
                        "\n"
                        "#if defined(__GNUC__)\n"
                        "#define WEAK_ATTR __attribute__ ((weak))\n"
                        "#else\n"
                        "#define WEAK_ATTR\n"
                        "#endif\n"
                        "\n")
        return

    ANON_COUNT = 0

    def resolveRef(self, ref, td = False):
        if VERBOSITY > 1:
            sys.stderr.write("trace: resolveRef -- %s %d\n" % (ref, td))
            pass
        t = self.ns[ref]
        done = False
        while not done:
            if td and isinstance(t, StabsTypeDef):
                t = t.type
                pass
            elif isinstance(t, StabsTypeRef) and t != self.ns[t.ref]:
                t = self.ns[t.ref]
                pass
            else:
                done = True
                pass
            pass

        if VERBOSITY > 1:
            sys.stderr.write("trace: resolveRef out -- %s\n" % `t`)
            pass

        return t

    def explodeType(self, t, dst):
        if VERBOSITY > 1:
            sys.stderr.write("trace: explodeType -- %s\n" % `t`)
            pass

        if t in dst:
            pass
        elif isinstance(t, StabsTypeRef) and t != self.ns[t.ref]:
            rt = self.resolveRef(t.ref)
            if (t.name and
                not t.name.startswith("__reflecxx") and
                rt.name != t.name):
                rt = copy.deepcopy(rt)
                rt.name = t.name
                pass
            self.explodeType(rt, dst)
            pass
        elif isinstance(t, StabsArray):
            if t not in dst:
                dst.append(t)
                pass
            self.explodeType(self.resolveRef(t.elem_id), dst)
            pass
        elif isinstance(t, StabsStruct):
            if t not in dst:
                dst.append(t)
                pass
            for f in t.fields:
                self.explodeType(f, dst)
                pass
            pass
        elif isinstance(t, StabsStructField):
            self.explodeType(self.resolveRef(t.id), dst)
            pass
        elif isinstance(t, StabsPointer):
            if t not in dst:
                dst.append(t)
                pass
            self.explodeType(self.resolveRef(t.id), dst)
            pass
        elif isinstance(t, StabsFunction):
            if t not in dst:
                dst.append(t)
                pass
            self.explodeType(t.ret_type, dst)
            for p in t.params:
                self.explodeType(p.type, dst)
                pass
            pass
        else:
            dst.append(t)
            pass

        return

    def formatRef(self, ref):
        if VERBOSITY > 1:
            sys.stderr.write("trace: formatRef -- %s\n" % ref)
            pass
        
        if ref not in self.ns:
            sys.stderr.write("error: cannot find ref -- %s\n" % ref)
            sys.exit(1)
            pass

        t = self.resolveRef(ref, True)
        
        if t.name in StabsToC.PRIMITIVES:
            retval = "{ %s, { 0 } }" % StabsToC.PRIMITIVES[t.name]
            pass
        elif isinstance(t, StabsEnum):
            retval = "{ RT_ENUM, { &%s_sub_%s } }" % (t.name, EXTENSION)
            pass
        elif isinstance(t, StabsStruct):
            retval = "{ RT_STRUCT, { &%s_sub_%s } }" % (
                t.name, EXTENSION)
            pass
        elif isinstance(t, StabsPointer):
            if self.resolveRef(t.id, True).name == "char":
                rt = "RT_STRING"
                pass
            else:
                rt = "RT_POINTER"
                pass
            retval = "{ %s, { &%s_sub_%s } }" % (rt, t.name, EXTENSION)
            pass
        else:
            retval = "{ (reflecxx_type_t)0, 0 }"
            pass
        
        return retval

    def formatCinv(self, ref):
        
        t = self.resolveRef(ref, True)

        if t.name in StabsToC.CINV_PRIMITIVES:
            retval = StabsToC.CINV_PRIMITIVES[t.name]
            pass
        elif isinstance(t, StabsEnum):
            retval = "i"
            pass
        else:
            retval = "p"
            pass
        
        return retval

    def formatField(self, field):
        retval = '{ "%s", %s, %d, %d }' % (
            field.name, self.formatRef(field.id), field.bitoff,
            field.bitsize)

        return retval

    def formatElement(self, elem):
        return '{ "%s", %d }' % (elem.name, elem.value)

    def formatParam(self, param):
        return '{ "%s", %s }' % (param.name, self.formatRef(param.type.ref))

    def printTypes(self, ts):
        if VERBOSITY > 1:
            sys.stderr.write("trace: printTypes -- %s\n" % `ts`)
            pass
        
        nts = []
        for t in ts:
            self.explodeType(t, nts)
            pass
        
        self.decl = True
        for t in nts:
            self.printType(t)
            pass
        self.decl = False
        for t in nts:
            self.printType(t)
            pass
        
        return

    def printTypeDef(self, t):
        self.printType(t.type)
        return

    def printArray(self, t):
        if self.decl:
            self.file.write(DECL % (t.name, EXTENSION))
            self.file.write(
                "extern struct reflecxx_array %s_sub_%s WEAK_ATTR;\n" % (
                t.name, EXTENSION))
            pass
        else:
            self.file.write(
                "\n"
                "struct reflecxx_array %s_sub_%s = {\n"
                "    %s,\n"
                "    %s,\n"
                "    %s\n"
                "};\n"
                "struct reflecxx_full_type %s_%s = {\n"
                "    RT_ARRAY,\n"
                "    { &%s_sub_%s },\n"
                "};\n" % (
                t.name, EXTENSION,
                t.lo,
                t.hi,
                self.formatRef(t.elem_id),
                t.name, EXTENSION,
                t.name, EXTENSION))
            pass
        return

    def printPointer(self, t):
        if self.decl:
            self.file.write(DECL % (t.name, EXTENSION))
            self.file.write(
                "extern struct reflecxx_pointer %s_sub_%s WEAK_ATTR;\n" % (
                t.name, EXTENSION))
            pass
        else:
            self.file.write(
                "\n"
                "struct reflecxx_pointer %s_sub_%s = { %s };\n"
                "struct reflecxx_full_type %s_%s = {\n"
                "    RT_POINTER,\n"
                "    { &%s_sub_%s }\n"
                "};\n" % (
                t.name, EXTENSION, self.formatRef(t.id),
                t.name, EXTENSION,
                t.name, EXTENSION))
            pass
        return

    def printTypeRef(self, t):
        if t.ref not in self.ns:
            sys.stderr.write("error: unknown ref -- %s\n", t.ref)
            sys.exit(1)
            pass

        # self.printType(self.ns[t.ref])
        return

    def printStruct(self, t):
        if self.decl:
            self.file.write(DECL % (t.name, EXTENSION))
            self.file.write(
                "extern struct reflecxx_struct %s_sub_%s WEAK_ATTR;\n" % (
                t.name, EXTENSION))
            pass
        else:
            self.file.write(
                "\n"
                "static struct reflecxx_field %s_field_info[] = {\n"
                "    %s\n"
                "};\n"
                "\n"
                "struct reflecxx_struct %s_sub_%s = {\n"
                "    %d,\n"
                "    %d,\n"
                "    %s_field_info,\n"
                "};\n"
                "struct reflecxx_full_type %s_%s = {\n"
                "    RT_STRUCT,\n"
                "    { &%s_sub_%s },\n"
                "};\n" % (
                t.name,
                ",\n    ".join([self.formatField(f) for f in t.fields]),
                t.name, EXTENSION,
                t.size,
                len(t.fields),
                t.name,
                t.name, EXTENSION,
                t.name, EXTENSION))
            pass
        return
    
    def printEnum(self, t):
        if self.decl:
            self.file.write(DECL % (t.name, EXTENSION))
            self.file.write(
                "extern struct reflecxx_enum %s_sub_%s WEAK_ATTR;\n" % (
                t.name, EXTENSION))
            pass
        else:
            self.file.write(
                "\n"
                "static struct reflecxx_enum_element %s_element_info[] = {\n"
                "    %s\n"
                "};\n"
                "\n"
                "struct reflecxx_enum %s_sub_%s = {\n"
                "    %d,\n"
                "    %s_element_info\n"
                "};\n"
                "struct reflecxx_full_type %s_%s = {\n"
                "    RT_ENUM,\n"
                "    { &%s_sub_%s }\n"
                "};\n" % (
                t.name,
                ",\n    ".join([self.formatElement(e) for e in t.elements]),
                t.name, EXTENSION,
                len(t.elements),
                t.name,
                t.name, EXTENSION,
                t.name, EXTENSION))
            pass
        return

    def printFunction(self, t):
        if self.decl:
            self.file.write(DECL % (t.name, EXTENSION))
            self.file.write(
                "extern struct reflecxx_function %s_sub_%s WEAK_ATTR;\n" % (
                t.name, EXTENSION))
            pass
        else:
            self.file.write(
                "\n"
                "static struct reflecxx_param %s_param_info[] = {\n"
                "    %s\n"
                "};\n"
                "\n"
                "struct reflecxx_function %s_sub_%s = {\n"
                "    %s,\n"
                "    %d,\n"
                "    %s_param_info,\n"
                "    \"%s\",\n"
                "    \"%s\",\n"
                "};\n"
                "struct reflecxx_full_type %s_%s = {\n"
                "    RT_FUNCTION,\n"
                "    { &%s_sub_%s }\n"
                "};\n" % (
                t.name,
                ",\n    ".join([self.formatParam(p) for p in t.params]),
                t.name, EXTENSION,
                self.formatRef(t.ret_type.ref),
                len(t.params),
                t.name,
                self.formatCinv(t.ret_type.ref),
                "".join([self.formatCinv(p.type.ref) for p in t.params]),
                t.name, EXTENSION,
                t.name, EXTENSION))
            pass
        return

    def printRange(self, t):
        return

    def printType(self, t):
        if VERBOSITY > 1:
            sys.stderr.write("trace: printType -- %s\n" % `t`)
            pass

        if t:
            cname = t.__class__.__name__
            mname = re.sub(r'Stabs(\w+)', r'print\1', cname)
            if hasattr(self, mname):
                meth = getattr(self, mname)
                meth(t)
                pass
            else:
                sys.stderr.write("error: missing method -- %s\n" % mname)
                pass
            pass
        
        return
    
    pass

class Stabs:
    N_GSYM = 0x20
    N_FNAME = 0x22
    N_FUN = 0x24
    N_LSYM = 0x28
    N_SO = 0x64
    
    N_SO_AS = 0x1
    N_SO_C = 0x2
    N_SO_ANSI_C = 0x3
    N_SO_CC = 0x4
    N_SO_FORTRAN = 0x5
    N_SO_PASCAL = 0x6
    N_SO_FORTRAN90 = 0x7
    N_SO_OBJC = 0x32
    N_SO_OBJCPLUS = 0x33

    def __init__(self, ls):
        self.str = ls[0]
        self.type, self.other, self.desc, self.value = [int(x) for x in ls[1:]]
        return

    def __str__(self):
        return '.stabs  "%s",%d,%d,%d,%d' % (
            self.str, self.type, self.other, self.desc, self.value)

    pass

class AsmToStabs:
    def __init__(self, sp, pr):
        self.sp = sp
        self.possible_refs = pr
        return
    
    def parse(self, file):
        retval = []
        fp = open(file, 'r')
        for line in fp:
            m = re.search(r'.stabs\s+"([^"]+)",(\d+),(\d+),(\d+),(\d+)', line)
            if m:
                st = Stabs(m.groups())
                try:
                    retval.append(self.sp.parse(st.str))
                    pass
                except StabsParserException, e:
                    if VERBOSITY > 0:
                        sys.stderr.write("%s\n" % str(e))
                        pass
                    pass
                pass
            
            m = re.search(r'.indirect_symbol _(.+)_%s' % EXTENSION, line)
            if m:
                self.possible_refs.append(m.group(1))
                pass
            pass
        return retval
    
    pass

class ObjDumpToStabs:
    def __init__(self, sp, pr):
        self.sp = sp
        self.possible_refs = pr
        return

    def parse(self, file):
        global OBJDUMP_FORMATS
        
        retval = []
        pi = os.popen('objdump -G --syms %s' % file, 'r')
        for line in pi:
            m = None
            for of in OBJDUMP_FORMATS:
                m = re.match(of["pattern"], line)
                if m:
                    st = of["converter"](m)
                    try:
                        retval.append(self.sp.parse(st.str))
                        OBJDUMP_FORMATS = [ of ]
                        pass
                    except StabsParserException, e:
                        if VERBOSITY > 0:
                            sys.stderr.write("%s\n" % str(e))
                            pass
                        pass
                    break
                pass

            if not m:
                m = re.search(
                    r'[\w\d]+\s+g?\s+\*UND\*\s*(?:[\w\d]+)?\s+_?(.+)_%s' %
                    EXTENSION,
                    line)
                if m:
                    self.possible_refs.append(m.group(1))
                    pass
                pass
            pass
        rc = pi.close()
        if rc:
            sys.stderr.write("warn: objdump exited with %d\n" % rc)
            pass
        
        return retval

    pass

def usage():
    print "usage: reflecxx-gen [-h] file1 [file2 ...]"
    print
    print "Generate reflection data from the debugging information in the"
    print "given object/asm files."
    print
    print "Options:"
    print "  -h        This help message."
    print "  -V        Print version information."
    print "  -v        Increase verbosity, this is additive."
    print "  -e ext    The variable name extension for generated type"
    print "            information.  (Default: %s)" % EXTENSION
    print
    print "Example:"
    print "    $ reflecxx-gen foo.o bar.o > reflecxx_info.c"
    return

try:
    opts, req_args = getopt.getopt(sys.argv[1:],
                                   "he:vV",
                                   [ "help" ])

    for opt, val in opts:
        if opt in ("-h", "--help"):
            usage()
            sys.exit()
            pass
        elif opt in ("-e",):
            if len(val) == 0:
                raise getopt.error("extension cannot be empty", "-e")
            if not re.match(r'^[a-zA-Z_0-9]+$', val):
                raise getopt.error("extension must be a valid C variable name")
            
            EXTENSION = val
            pass
        elif opt in ("-v",):
            VERBOSITY += 1
            pass
        elif opt in ("-V",):
            print PACKAGE_VERSION
            sys.exit()
            pass
        pass

    if len(req_args) < 1:
        raise getopt.error("not enough arguments")

    not_handled_files = [f for f in req_args
                         if os.path.splitext(f)[1] not in FILE_EXTS]

    if len(not_handled_files) > 0:
        raise getopt.error(
            "cannot handle -- %s" % ", ".join(not_handled_files))

    pass
except getopt.error, e:
    sys.stderr.write("error: %s\n" % e.args[0])
    usage()
    sys.exit(2)
    pass

ns = {}
types = []
possible_refs = []
sp = StabsParser(ns)
a2s = AsmToStabs(sp, possible_refs)
o2s = ObjDumpToStabs(sp, possible_refs)

ext2p = {
    ".s" : a2s,
    ".o" : o2s,
    }

try:
    for file in req_args:
        base, ext = os.path.splitext(file)
        parser = ext2p[ext]
        types.extend(parser.parse(file))
        pass

    gen_types = []
    for t in types:
        if VERBOSITY > 2:
            sys.stderr.write("trace: type -- %s\n" % t.name)
            pass
        
        if t.name in possible_refs:
            gen_types.append(t)
            possible_refs.remove(t.name)
            pass
        pass
    
    for key in ns:
        value = ns[key]
        if (isinstance(value, StabsTypeRef) and
            value.ref in ns and
            not ns[value.ref].name):
            ns[value.ref].name = value.name
            pass
        pass

    for key in ns:
        value = ns[key]
        if not value.name:
            value.name = value.anon_name()
            pass
        pass

    s2c = StabsToC(sys.stdout, ns)
    s2c.printTypes(gen_types)
    
    for pr in possible_refs:
        sys.stderr.write(
            "warn: did not generate data for possible ref -- %s_%s\n" % (
            pr, EXTENSION))
        pass
    pass
except OSError, e:
    sys.stderr.write("error: %s\n" % e.args[0])
    pass
